import { Service } from "egg";
import * as Y from "yjs";

/**
 * y-mysql 适配器服务（最小实现）
 * 作用：
 * - 提供 Yjs 文档在 MySQL 的读写能力（快照 + 增量）
 * - 支持 applyUpdate 与 merge，便于断线重连/强制同步
 */
export default class YjsMysqlService extends Service {
  /** 读取完整快照（不存在则返回 null） */
  public async getYjsDocument(documentId: string, roomName: string): Promise<Uint8Array | null> {
    const { models } = this.app as any;
    const row = await models.YjsDocument.findOne({ where: { document_id: documentId, room_name: roomName } });
    if (!row || !row.yjs_state) return null;
    // Buffer -> Uint8Array
    return new Uint8Array(row.yjs_state as Buffer);
  }

  /** 保存完整快照（可带增量历史） */
  public async saveYjsDocument(
    documentId: string,
    roomName: string,
    state: Uint8Array,
    updates?: Uint8Array[]
  ): Promise<boolean> {
    const { models } = this.app as any;
    const existed = await models.YjsDocument.findOne({ where: { document_id: documentId, room_name: roomName } });
    const payload = {
      document_id: documentId,
      room_name: roomName,
      yjs_state: Buffer.from(state),
      yjs_updates: updates ? updates.map(u => Array.from(u)) : existed?.yjs_updates ?? [],
      last_updated: new Date(),
      version: existed ? existed.version + 1 : 1
    } as any;
    if (existed) await existed.update(payload); else await models.YjsDocument.create(payload);
    return true;
  }

  /**
   * 应用增量到数据库：读取现态 -> applyUpdate -> 保存新态，并追加进 updates
   */
  public async applyUpdate(documentId: string, roomName: string, update: Uint8Array): Promise<boolean> {
    const { models } = this.app as any;
    const existed = await models.YjsDocument.findOne({ where: { document_id: documentId, room_name: roomName } });
    const doc = new Y.Doc();
    if (existed?.yjs_state) {
      Y.applyUpdate(doc, new Uint8Array(existed.yjs_state as Buffer));
    }
    Y.applyUpdate(doc, update);
    const newState = Y.encodeStateAsUpdate(doc);
    const updates: Uint8Array[] = existed?.yjs_updates
      ? [...(existed.yjs_updates as any[]).map((u: number[]) => new Uint8Array(u)), update]
      : [update];
    await this.saveYjsDocument(documentId, roomName, newState, updates);
    doc.destroy();
    return true;
  }

  /** 合并多个状态：常用于强制同步 */
  public async mergeDocumentStates(
    documentId: string,
    roomName: string,
    states: Uint8Array[]
  ): Promise<boolean> {
    const doc = new Y.Doc();
    for (const s of states) {
      Y.applyUpdate(doc, s);
    }
    const merged = Y.encodeStateAsUpdate(doc);
    await this.saveYjsDocument(documentId, roomName, merged);
    doc.destroy();
    return true;
  }
}


